/*******************************************************************************
* Signavio Core Components
* Copyright (C) 2012 Signavio GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.oryxeditor.server.diagram.generic;
import java.util.ArrayList;
import java.util.List;
import org.oryxeditor.server.diagram.Bounds;
import org.oryxeditor.server.diagram.Point;
/**
* Represents an edge element in a diagram.
*
* @author Philipp Maschke, Robert Gurol
*
* @param <S> the actual type of shape to be used (must inherit from {@link GenericShape}); calls to {@link #getChildShapesReadOnly()}, ... will return this type
* @param <D> the actual type of diagram to be used (must inherit from {@link GenericDiagram}); {@link #getDiagram()} will return this type
*/
public abstract class GenericEdge<S extends GenericShape<S,D>, D extends GenericDiagram<S,D>> extends GenericShapeImpl<S, D> {
protected S target;
protected S source;
public GenericEdge(String resourceId) {
super(resourceId);
}
public GenericEdge(String resourceId, String stencilId) {
super(resourceId, stencilId);
}
/* getters & setters */
/**
* Returns the source of this shape; usually a node.<br>
*
*
* @return the source shape of this shape
*/
public S getSource() {
return this.source;
}
/**
* Set the source of a shape</br>
* The source is also set as an incoming shape of this Edge.</br>
* If there is a current source, it is replaced and also removed from the
* incoming shapes of this edge.
*
* @param shape
*
* @deprecated Use {@link #connectToASource(GenericShape)} instead
*/
public void setSourceAndUpdateIncomings(S shape) {
if (this.getSource() != null) {
this.removeIncoming(this.getSource());
}
this.source = shape;
if (shape != null)
this.addIncoming(shape);
}
/**
* Gives the target of shape, which defined another associated shape
*
* @return the target shape
*/
public S getTarget() {
return target;
}
/**
* Set a (new) target shape for a shape. <br/>
* The target is also set as an outgoing shape of this Edge. <br/>
* If there is a current target, it is replaced and also removed from the
* outgoing shapes of this edge.
*
* @param target
* the target shape to set
*
* @deprecated Use {@link #connectToATarget(GenericShape)} instead
*/
public void setTargetAndUpdateOutgoings(S target) {
if (this.getTarget() != null) {
this.removeOutgoing(this.getTarget());
}
this.target = target;
if (target != null)
this.addOutgoing(target);
}
/**
* Connects to a source, updates the source's outgoing shapes as well.
* Disconnects from the previous source first, as
* {@link #disconnectFromSource()} would.
*
* @param newSource
* the shape to connect to
*/
public void connectToASource(S newSource) {
if (this.source != null)
this.removeIncomingAndUpdateItsOutgoings(this.source);
this.source = newSource;
if(newSource != null)
this.addIncomingAndUpdateItsOutgoings(newSource);
}
/**
* Disconnects the Edge from its source, updates the source's outgoing
* shapes as well. If there is no source (source equals null), nothing is
* done.
*
* @return true if it has actually disconnected
*/
public boolean disconnectFromSource() {
if (this.source == null)
return false;
this.removeIncomingAndUpdateItsOutgoings(this.source);
this.source = null;
return true;
}
/**
* Connects to a target, updates the target's incoming shapes as well.
* Disconnects from the previous target first, as
* {@link #disconnectFromTarget()} would.
*
* @param s
* the shape to connect to
*/
public void connectToATarget(S newTarget) {
if (this.target != null)
this.removeOutgoingAndUpdateItsIncomings(this.target);
this.target = newTarget;
if(newTarget != null)
this.addOutgoingAndUpdateItsIncomings(newTarget);
}
/**
* Disconnects the Edge from its target, updates the target's incoming
* shapes as well.
*
* @return true if it has actually disconnected
*/
public boolean disconnectFromTarget() {
if (this.target == null)
return false;
this.removeOutgoingAndUpdateItsIncomings(this.target);
this.target = null;
return true;
}
/**
* Add p as new last docker; updates the edge's bounds.
* @param p
*/
@Override
public void addDocker(Point p){
super.addDocker(p);
this.updateBounds();
}
/**
* Add p as docker at a given position; updates the edge's bounds.
*
* @param p
* @param position
*/
@Override
public void addDocker(Point p, int position){
super.addDocker(p, position);
this.updateBounds();
}
@Override
public void setDockers(List<Point> dockers) {
super.setDockers(dockers);
this.updateBounds();
}
protected void updateBounds() {
double maxX = Integer.MIN_VALUE;
double maxY = Integer.MIN_VALUE;
double minX = Integer.MAX_VALUE;
double minY = Integer.MAX_VALUE;
// position of first and last docker may be relative! have to get the
// absolute value and then calculate!
List<Point> l = new ArrayList<Point>(this.getDockersReadOnly());
if (l != null) {
if (this.getSource() != null) {
// relative coordinates!
if (this.getDockersReadOnly().size() != 0) {
Point p = this.getSource().getAbsoluteBounds()
.getUpperLeft();
p.add(this.getDockersReadOnly().get(0));
l.set(0, p);
}
}
if (this.getTarget() != null) {
// in case of 1 docker (which should not happen with an edge,
// anyway), the one docker is the source docker
if (this.getDockersReadOnly().size() > 1) {
Point p = this.getTarget().getAbsoluteBounds()
.getUpperLeft();
p.add(this.getDockersReadOnly().get(this.getDockersReadOnly().size() - 1));
l.set(this.getDockersReadOnly().size() - 1, p);
}
}
for (Point p : l) {
if (p == null)
continue;
if (p.getX() < minX)
minX = p.getX();
if (p.getY() < minY)
minY = p.getY();
if (p.getX() > maxX)
maxX = p.getX();
if (p.getY() > maxY)
maxY = p.getY();
}
}
this.setBounds(new Bounds(new Point(minX, minY), new Point(maxX, maxY)));
}
}